package clickhouse.service.impl;

import clickhouse.config.DataConfig;
import clickhouse.service.ITemplatePreparedStatement;
import clickhouse.service.ITemplateStatement;
import clickhouse.service.common.CommonUtil;
import clickhouse.util.StrUtil;

import ru.yandex.clickhouse.BalancedClickhouseDataSource;
import ru.yandex.clickhouse.ClickHouseConnection;
import ru.yandex.clickhouse.settings.ClickHouseProperties;


import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;

/**
 * ClickHouse操作实现类
 * 通过普通方式
 */
public class ClickHouseJdbcCommon extends ClickHouseDataBase {
	//连接池，支持多实例服务地址
	private static BalancedClickhouseDataSource dataSource;
	private static void init() {
		if (dataSource != null) {
			return;
		}		
		String url = String.format("jdbc:clickhouse://%s:%s/%s", DataConfig.getProperty("clickhouse.ip"),
				DataConfig.getProperty("clickhouse.port"), DataConfig.getProperty("clickhouse.db"));
		String user = DataConfig.getProperty("clickhouse.user");
		String password = DataConfig.getProperty("clickhouse.password");
		ClickHouseProperties properties = new ClickHouseProperties();
		// set connection options - see more defined in ClickHouseConnectionSettings
		// properties.setClientName("Agent #1");
		// set default request options - more in ClickHouseQueryParam
		properties.setSessionId("default-session-id");
		properties.setUser(user);
		properties.setPassword(password);
		dataSource = new BalancedClickhouseDataSource(url, properties);
	}

	static {
		init();
	}
    private ClickHouseJdbcCommon(){
        System.out.println("jdbc官方驱动方式实现");
    }

    /**
     * 构造带参数sql
     * @param tableName 表名
     * @param fieldNames 待查询的字段名集合
     * @param columnIndex 返回的字段序号与字段名映射
     * @return
     */
    private String getSqlForParams(String tableName,Set<String> fieldNames,Map<String,Integer> columnIndex){
        StringBuffer fields=new StringBuffer();
        StringBuffer ask=new StringBuffer();
        //字段序号与字段名的映射
        int index=1;
        for(String fieldname:fieldNames){
            fields.append(fieldname);
            fields.append(",");
            ask.append("?,");
            columnIndex.put(fieldname,index++);
        }
        String fieldsStr=fields.toString().substring(0,fields.length()-1);
        String askStr=ask.toString().substring(0,ask.length()-1);
        return String.format("insert into %s (%s) values(%s)",tableName,fieldsStr,askStr);
    }

    @Override
    public ClickHouseWrap getClickHouseWrap() {
        try {
            ClickHouseConnection conn = dataSource.getConnection();
            return new ClickHouseWrap(conn);
        }catch (Exception ex){
            ex.printStackTrace();
            throw new RuntimeException(ex.getMessage());
        }
    }

    @Override
    public void closeDataBase(ClickHouseWrap connection) {
        if(connection!=null){
            connection.close();
        }
    }
    /**
     * 批量插入数据
     * @param tableName 表名
     * @param fieldValues List<Map<字段名，字段值>>
     */
    @Override
    public void batchInsertData(ClickHouseWrap clickHouseWrap,String tableName,List<Map<String,Object>> fieldValues){
        CommonUtil.checkValidateX(tableName,fieldValues);
        Map<String,Integer> columnIndex=new HashMap<>();
        String sql=getSqlForParams(tableName,fieldValues.get(0).keySet(),columnIndex);
        clickHouseWrap.query("select * from " + tableName + " limit 1", new ITemplateStatement<Object>() {
            @Override
            public Object doOperation(Statement statement,ResultSet resultSet) throws SQLException {
                return clickHouseWrap.preparedQuery(sql, new ITemplatePreparedStatement() {
                    @Override
                    public Object doOperation(PreparedStatement preparedStatement) throws SQLException {
                        return batchInsertData(tableName,preparedStatement,resultSet,fieldValues,columnIndex);
                    }
                });
            }
        });

    }
    private Object batchInsertData(String tableName,Statement statement,ResultSet resultSet,List<Map<String,Object>> fieldValues,Map<String,Integer> columnIndex)throws SQLException{
        Map<String,Object> values;
        int tempColumnIndex;
        PreparedStatement preparedStatement=(PreparedStatement)statement;
        Map<String,String> fieldDef=getTableFieldMeta(tableName,resultSet.getMetaData());
        String columnType;
        for(int row=0;row<fieldValues.size();row++){
            values=fieldValues.get(row);
            for(Map.Entry<String,Object> rowV:values.entrySet()){
                tempColumnIndex=columnIndex.get(rowV.getKey());
                columnType=fieldDef.get(rowV.getKey());
                preparedStatement.setObject(tempColumnIndex,convertValue(rowV.getValue(),columnType));
            }
            preparedStatement.addBatch();
        }
        int[] r=preparedStatement.executeBatch();
        return r;
    }
    @Override
    public void batchInsertData(String tableName,List<Map<String,Object>> fieldValues) {
        CommonUtil.checkValidateX(tableName,fieldValues);
        ClickHouseWrap clickHouseWrap=null;
        try
        {
            clickHouseWrap=this.getClickHouseWrap();
            this.batchInsertData(clickHouseWrap,tableName,fieldValues);
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            this.closeDataBase(clickHouseWrap);
        }
    }
    @Override
    public List<Map<String, Object>> getListMapFromSql(ClickHouseWrap clickHouseWrap,String sql) {
        return clickHouseWrap.query(sql, new ITemplateStatement<List<Map<String, Object>>>() {
            @Override
            public List<Map<String, Object>> doOperation(Statement statement, ResultSet resultSet){
                return retriveDataFromResultSet(resultSet);
            }
        });
    }
    @Override
    public List<Map<String, Object>> getListMapFromSql(String sql) {
        ClickHouseWrap clickHouseWrap=null;
        try
        {
            clickHouseWrap=this.getClickHouseWrap();
            return this.getListMapFromSql(clickHouseWrap,sql);
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            this.closeDataBase(clickHouseWrap);
        }
        return Collections.emptyList();
    }
    @Override
    public  int deleteData(ClickHouseWrap clickHouseWrap,String tableName,String clause){
        if(StrUtil.isNull(tableName)||StrUtil.isNull(clause)){
            throw new RuntimeException("参数有误");
        }
        return clickHouseWrap.query(null, new ITemplateStatement<Integer>() {
            @Override
            public Integer doOperation(Statement statement, ResultSet resultSet) throws SQLException {
                String sql=String.format("ALTER TABLE %s DELETE WHERE %s",tableName,clause);
                return statement.executeUpdate(sql);
            }
        });
    }
    @Override
    public  int deleteData(String tableName,String clause){
        if(StrUtil.isNull(tableName)||StrUtil.isNull(clause)){
            throw new RuntimeException("参数有误");
        }
        ClickHouseWrap clickHouseWrap=null;
        try
        {
            clickHouseWrap=this.getClickHouseWrap();
            return this.deleteData(clickHouseWrap,tableName,clause);
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            this.closeDataBase(clickHouseWrap);
        }
        return -1;
    }
    @Override
    public int updateData(ClickHouseWrap clickHouseWrap,String tableName,Map<String,Object> updateValues,String clause){
        if(StrUtil.isNull(tableName)
                ||updateValues==null
                ||updateValues.size()==0
                ||StrUtil.isNull(clause)){
            throw new RuntimeException("参数有误");
        }
        return clickHouseWrap.query(String.format("select * from %s limit 1",tableName), new ITemplateStatement<Integer>() {
            @Override
            public Integer doOperation(Statement statement, ResultSet resultSet) throws SQLException{
                Map<String,String> fieldMeta=getTableFieldMeta(tableName,resultSet.getMetaData());
                String updateStr=constructUpdateStr(updateValues,fieldMeta);
                String sql=String.format("alter table %s update %s where %s",tableName,updateStr,clause);
                return statement.executeUpdate(sql);
            }
        });
    }
    @Override
    public int updateData(String tableName,Map<String,Object> updateValues,String clause){
        if(StrUtil.isNull(tableName)
                ||updateValues==null
                ||updateValues.size()==0
                ||StrUtil.isNull(clause)){
            throw new RuntimeException("参数有误");
        }
        ClickHouseWrap clickHouseWrap=null;
        try
        {
            clickHouseWrap=this.getClickHouseWrap();
            return this.updateData(clickHouseWrap,tableName,updateValues,clause);
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            this.closeDataBase(clickHouseWrap);
        }
        return -1;
    }
}
